package sg.edu.ntu.metaweb.storage;

import java.io.IOException;
import java.net.URL;

import rice.Continuation;
import rice.p2p.commonapi.Id;
import rice.p2p.commonapi.Message;
import rice.p2p.past.PastContent;
import rice.p2p.past.gc.GCPast;
import rice.p2p.past.gc.GCPastImpl;
import rice.persistence.LRUCache;
import rice.persistence.MemoryStorage;
import rice.persistence.PersistentStorage;
import rice.persistence.Storage;
import rice.persistence.StorageManagerImpl;
import sg.edu.ntu.metaweb.pastry.MetawebCache;
import sg.edu.ntu.metaweb.pastry.MetawebGCPastContent;
import sg.edu.ntu.metaweb.pastry.MetawebPastPolicy;
import sg.edu.ntu.metaweb.pastry.PastryNodeHandle;
import sg.edu.ntu.metaweb.pastry.messages.RequestRateWebpageMessage;
import sg.edu.ntu.metaweb.pastry.messages.RequestWebpageDataMessage;
import sg.edu.ntu.metaweb.pastry.messages.ResponseWebpageDataMessage;
import sg.edu.ntu.metaweb.storage.data.Webpage;

public class StorageManager {

	private PastryNodeHandle pastryNodeHandle;
	private GCPast gcPastHandle;
	
	public StorageManager(PastryNodeHandle pastryNodeHandle) {
		
		this.pastryNodeHandle = pastryNodeHandle;
		
		try {
		    String storageDirectory = "./storage" + this.pastryNodeHandle.pastryNode.getId().hashCode();
		    Storage storage = new PersistentStorage(pastryNodeHandle.pastryIdFactory, storageDirectory, 4 * 1024 * 1024, this.pastryNodeHandle.pastryNode.getEnvironment());
	
		    this.gcPastHandle = new GCPastImpl(this.pastryNodeHandle.pastryNode, 
	 			  		 					   new StorageManagerImpl(pastryNodeHandle.pastryIdFactory, storage, new LRUCache(new MemoryStorage(pastryNodeHandle.pastryIdFactory), 512 * 1024, this.pastryNodeHandle.pastryNode.getEnvironment())), 
	 			  		 					   new MetawebCache(), 
	 			  		 					   5, 
	 			  		 					   "metaweb", 
	 			  		 					   new MetawebPastPolicy(), 
	 			  		 					   10000000, 
	 			  		 					   new StorageManagerImpl(pastryNodeHandle.pastryIdFactory, storage, new LRUCache(new MemoryStorage(pastryNodeHandle.pastryIdFactory), 512 * 1024, this.pastryNodeHandle.pastryNode.getEnvironment())));
   
		}
		catch (IOException exc) {
			System.err.println("Cannot initiate storage");
			exc.printStackTrace();
		}
	}
	
	
	
	public void requestWebpageData(RequestWebpageDataMessage msg) {
		readWebpage(msg.getUrl(), msg);
	}
	
	public void responseWebpageData(MetawebGCPastContent result, RequestWebpageDataMessage msg) {
		String webpageData = "<webpage></webpage>";
		
		if (result != null)
			webpageData = result.getWebpage().toXMLString();

		// return result to requesting peer (sender)
		ResponseWebpageDataMessage responseMsg = new ResponseWebpageDataMessage(pastryNodeHandle.pastryApplication.getNode().getId(), msg.getTargetID(), msg.getRequestID(), webpageData);
		pastryNodeHandle.sendMessage(msg.getSenderID(), responseMsg);
	}
	
	
	
	
	
	public void requestRateWebpage(RequestRateWebpageMessage msg) {
		readWebpage(msg.getUrl(), msg);
	}
	
	public void responseRateWebpage(MetawebGCPastContent result, RequestRateWebpageMessage msg) {
		Webpage page = null;
		
		if (result == null)
			page = new Webpage(msg.getUrl().toString());
		
		this.writeWebpage(page);
	}
	
	private void writeWebpage(Webpage page) {
		
		// store locally on current node
		//final MetawebGCPastContent myContent = new MetawebGCPastContent(pastryNodeHandle.getPastryNode().getId(), page);
		final MetawebGCPastContent myContent = new MetawebGCPastContent(pastryNodeHandle.pastryIdFactory.buildId(page.getUrl().toString()), page);
		this.gcPastHandle.insert(myContent, new Continuation<Boolean[], Exception>() {
	        // the result is an Array of Booleans for each insert
	        public void receiveResult(Boolean[] results) {          
	          int numSuccessfulStores = 0;
	          for (int ctr = 0; ctr < results.length; ctr++) {
	            if (results[ctr].booleanValue()) 
	              numSuccessfulStores++;
	          }
	          System.out.println(myContent + " successfully stored at " + numSuccessfulStores + " locations.");
	          System.out.println("Insert Key: " + myContent.getId().toStringFull());
	        }
	  
	        public void receiveException(Exception result) {
	          System.out.println("Error storing key: " + myContent.getId().toStringFull());
	          result.printStackTrace();
	        }
	      });
	}
	
	
	
	private void readWebpage(URL url, Message msg) {
		
		final Id lookupKey = pastryNodeHandle.pastryIdFactory.buildId(url.toString());
		System.out.println("Lookup Key: " + lookupKey.toStringFull());
		RequestWebpageDataContinuation continuation = new RequestWebpageDataContinuation(this, msg);
		this.gcPastHandle.lookup(lookupKey, continuation); 

	}
	
}
